﻿using System.Collections.Generic;
using System.Linq;
using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel
{
    /// <summary>
    /// An undoable command for removing a connector.
    /// </summary>
    public class RemoveConnectorCommand : UndoableCommandBase
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="RemoveConnectorCommand"/> class.
        /// </summary>
        /// <param name="undoStack">The undo command stack</param>
        /// <param name="redoStack">The redo command stack</param>
        public RemoveConnectorCommand(CommandStack undoStack, CommandStack redoStack)
            : base(undoStack, redoStack)
        {
            AddParameter<Elements.CWNotation.Connector>("Connector", ParameterDirection.In);

            Name = "Remove Connector";

            removedConnectors = new List<Elements.CWNotation.Connector>();
        }

        #endregion

        #region Command Logic

        /// <inheritdoc/>
        protected override void CommandOperation(object parameter)
        {
            var modelConector = ((Model.Elements.CWNotation.Connector)Connector.AdaptedElement).PresentedConnector;
            var modelComposition = modelConector.FindOwner<Model.Elements.CompositionElement>();
            var modelPart = (Model.Packaging.CompositionPart)modelComposition.PackagePart;
            var project = Connector.FindOwner<Elements.Project>();

            modelPart.RaiseGlobalChangePending(modelConector.Start.FindOwner<Model.Elements.MeasureElement>(), modelConector.End.FindOwner<Model.Elements.MeasureElement>());

            var references = modelComposition.GetReferencingPages(modelConector.Start.FindOwner<Model.Elements.MeasureElement>(), modelConector.End.FindOwner<Model.Elements.MeasureElement>());
            foreach (var reference in references)
            {
                var score = project.Scores.First(s => s.AdaptedPart == reference.Page.Owner);
                var page = score.Pages.First(p => p.AdaptedPart == reference.Page);
                var section = page.Sections.First(s => s.AdaptedElement == reference.Section) as Elements.MusicSection;
                var interior = (Elements.CWNotation.MusicSectionInterior)section.Interior;
                var connectors = from system in interior.Systems
                                 from connector in system.Connectors
                                 where ((Model.Elements.CWNotation.Connector)connector.AdaptedElement).PresentedConnector == modelConector
                                 select connector;

                foreach (var connector in connectors)
                    removedConnectors.Add(connector);
            }

            modelComposition.Connectors.Remove(modelConector);

            project.IsModified = true;
        }

        /// <inheritdoc/>
        protected override void RedoOperation()
        {
            var modelConector = ((Model.Elements.CWNotation.Connector)Connector.AdaptedElement).PresentedConnector;
            var modelComposition = modelConector.FindOwner<Model.Elements.CompositionElement>();
            var project = Connector.FindOwner<Elements.Project>();

            modelComposition.Connectors.Remove(modelConector);

            project.IsModified = true;
        }

        /// <inheritdoc/>
        protected override void UndoOperation()
        {
            var modelConector = ((Model.Elements.CWNotation.Connector)Connector.AdaptedElement).PresentedConnector;
            var modelComposition = modelConector.FindOwner<Model.Elements.CompositionElement>();
            var project = Connector.FindOwner<Elements.Project>();

            modelComposition.Connectors.Add(modelConector);

            foreach (var connector in removedConnectors)
            {
                var system = connector.FindOwner<Elements.CWNotation.System>();
                var modelSystem = (Model.Elements.CWNotation.System)system.AdaptedElement;
                modelSystem.RestoreConnector((Model.Elements.CWNotation.Connector)connector.AdaptedElement);

                var vmConnector = system.Connectors.First(c => c.AdaptedElement == connector.AdaptedElement);
                system.Connectors[system.Connectors.IndexOf(vmConnector)] = vmConnector;

                if (system.FindOwner<Elements.Page>() != null)
                    Model.Elements.CWNotation.Layout.Layouter.LayoutHorizontally(modelSystem);
                else
                    Model.Elements.CWNotation.Layout.Layouter.LayoutHorizontallyNA(modelSystem);
            }

            project.IsModified = true;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the removed connector.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.CWNotation.Connector"/>
        /// </para>
        /// <para>
        /// The removed connector.<br/>
        /// This property wraps the "Connector" command parameter.
        /// </para>
        /// </value>
        public Elements.CWNotation.Connector Connector
        {
            get { return GetParameterValue<Elements.CWNotation.Connector>("Connector"); }
            set { SetParameterValue<Elements.CWNotation.Connector>("Connector", value); }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the list of removed connectors.
        /// </summary>
        private List<Elements.CWNotation.Connector> removedConnectors;

        #endregion
    }
}
